/**
 * Copyright (c) 2008, Andrew Carter All rights reserved. Redistribution and use
 * in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met: Redistributions of source
 * code must retain the above copyright notice, this list of conditions and the
 * following disclaimer. Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution. Neither
 * the name of Andrew Carter nor the names of contributors may be used to
 * endorse or promote products derived from this software without specific prior
 * written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.acarter.scenemonitor;

import java.lang.reflect.InvocationTargetException;
import java.util.logging.Logger;

import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import com.acarter.jmejtree.JMEComposableTree;
import com.acarter.scenemonitor.dialog.MonitorDialog;
import com.acarter.scenemonitor.information.A_MonitorInformationPanel;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;

/**
 * This exposes all the functionality to the user.
 * 
 * @author Andrew Carter
 */
public class SceneMonitor {

	/** Logger name */
	public static final String LOGGER_NAME = "com.acarter.scenemonitor.SceneMonitor";

	/** Logger reference */
	private static final Logger LOGGER = Logger.getLogger(SceneMonitor.LOGGER_NAME);

	/** The one and only instance if this class */
	private static SceneMonitor instance = null;

	/** The dialog displayed to the user */
	private MonitorDialog dialog = null;

	/**
	 * Private constructor.
	 * 
	 * @throws InvocationTargetException
	 * @throws InterruptedException
	 */
	private SceneMonitor() {

		Runnable runnable = new Runnable() {

			public void run() {

				try {

					UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
				}
				catch (Exception e) {

					LOGGER.warning("Could not set cross platform look and feel.");
					e.printStackTrace();
				}

				dialog = new MonitorDialog(null);
			}
		};

		executeSwingRunnable(runnable);
	}

	/**
	 * Returns the singleton instance.
	 * 
	 * @return The instance
	 */
	public static SceneMonitor getMonitor() {

		if(instance == null) {
			instance = new SceneMonitor();
		}

		return instance;
	}

	/**
	 * Set the visibility of the dialog.
	 * 
	 * @param show
	 *            True to show it, False to hide it.
	 */
	public void showViewer(final boolean show) {

		Runnable runnable = new Runnable() {

			public void run() {
				dialog.setVisible(show);
			}
		};

		executeSwingRunnable(runnable);
	}

	/**
	 * Returns whether the dialog is visible or not.
	 * 
	 * @return boolean True if visible, false otherwise.
	 */
	public boolean isVisible() {

		return dialog.isVisible();
	}

	/**
	 * Called by the host application to refresh the tree view in the dialog.
	 * 
	 * @param tpf
	 *            Time per frame
	 */
	public void updateViewer(final float tpf) {

		Runnable runnable = new Runnable() {

			public void run() {
				dialog.doUpdate(tpf);
			}
		};

		executeSwingRunnable(runnable);
	}

	/**
	 * Called by the host application to render from the dialog.
	 * 
	 * @param tpf
	 *            Time per frame
	 */
	public void renderViewer(final Renderer renderer) {

		// This needs to be done in the GL thread, so no swing invoke later
		// calls used here
		dialog.render(renderer);
	}

	/**
	 * Defines the amount of time between tree updates.
	 * 
	 * @param seconds
	 */
	public void setViewerUpdateInterval(final float seconds) {

		Runnable runnable = new Runnable() {

			public void run() {
				dialog.setUpdateInterval(seconds);
			}
		};

		executeSwingRunnable(runnable);
	}

	/**
	 * Adds a node to the list of nodes the user may select using the node name.
	 * 
	 * @param node
	 */
	public void registerNode(Node node) {

		registerNode(node, node.getName());
	}

	/**
	 * Adds a node to the list of nodes the user may select.
	 * 
	 * @param node
	 * @param displayName
	 *            The name to use in the list
	 */
	public void registerNode(final Node node, final String displayName) {

		Runnable runnable = new Runnable() {

			public void run() {
				dialog.registerNode(node, displayName);
			}
		};

		executeSwingRunnable(runnable);
	}

	/**
	 * Removes a node from the list of node the user may select.
	 * 
	 * @param node
	 */
	public void unregisterNode(final Node node) {

		Runnable runnable = new Runnable() {

			public void run() {
				dialog.unregisterNode(node);
			}
		};

		executeSwingRunnable(runnable);
	}

	/**
	 * Selects the given object in the tree.
	 * 
	 * @param object
	 *            The object to select.
	 */
	public void selectSceneObject(final Object object) {

		Runnable runnable = new Runnable() {

			public void run() {
				dialog.selectObject(object);
			}
		};

		executeSwingRunnable(runnable);
	}

	/**
	 * Returns the tree instance used by the Monitor dialog.
	 * 
	 * @return The tree instance
	 */
	public JMEComposableTree getMonitorTree() {

		return dialog.getTree();
	}

	/**
	 * Allows the addition of custom descriptor definitions.
	 * 
	 * @param classType
	 * @param descriptor
	 */
	public A_MonitorInformationPanel getMonitorInformationPanel() {

		return dialog.getInformationPanel();
	}
	
	/**
	 * Returns the Scene Monitor dialog.
	 * 
	 * @return The dialog
	 */
	public JDialog getMonitorDialog() {
		
		return dialog;
	}

	/**
	 * Executes a runnable in the Swing Event Dispatch thread. Checks if we are
	 * already in the EDT.
	 * 
	 * @param runnable
	 */
	private void executeSwingRunnable(Runnable runnable) {

		if(!SwingUtilities.isEventDispatchThread()) {

			try {
				SwingUtilities.invokeAndWait(runnable);
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
			catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		else
			runnable.run();
	}
	
	/**
	 * Performs necessary actions on shutdown of the application.
	 */
	public void cleanup() {
		
		dialog.dispose();
	}
}
